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_EXP,   "exp(#0)"},
00159   {SH_OP_EXP2,  "exp2(#0)"},
00160   {SH_OP_EXP10, "exp10(#0)"},
00161   {SH_OP_FLR,   "floor(#0)"},
00162   {SH_OP_FRAC,  "#0 - floor(#0)"},
00163   {SH_OP_LOG,   "log(#0)"},
00164   {SH_OP_LOG2,  "log2(#0)"},
00165   {SH_OP_LOG10, "log10(#0)"},
00166   {SH_OP_LRP,   "$0 * ($1 - $2) + $2"},
00167   {SH_OP_MAD,   "$0 * $1 + $2"},
00168   {SH_OP_MAX,   "($0 > $1 ? $0 : $1)"},
00169   {SH_OP_MIN,   "($0 < $1 ? $0 : $1)"}, 
00170   {SH_OP_MOD,   "($0 - $1 * floor((double)$0 / $1))"},
00171   {SH_OP_POW,   "pow($0, $1)"},
00172   {SH_OP_RCP,   "1 / #0"},
00173   {SH_OP_RND,   "floor(#0 + 0.5)"},
00174   {SH_OP_RSQ,   "1 / sqrt(#0)"},
00175   {SH_OP_SIN,   "sin(#0)"},
00176   {SH_OP_SGN,   "(#0 < 0 ? -1 : (#0 > 0 ? 1 : 0))"},
00177   {SH_OP_SQRT,  "sqrt(#0)"},
00178   {SH_OP_TAN,   "tan(#0)"},
00179   {SH_OP_COND,  "($0 > 0 ? $1 : $2)"},
00180   {SH_OP_FETCH, "#0"},
00181 
00182   {SH_OPERATION_END,  0} 
00183   // @todo LO, HI, SETLO, SETHI
00184 };
00185 
00186 // @todo type these are still implemented in the switch statement below
00187 // fix them later or maybe just leave them 
00188 #if 0
00189   {SH_OP_CMUL,             
00190   {SH_OP_CSUM,             
00191   {SH_OP_DOT,             
00192   {SH_OP_NORM,             
00193   {SH_OP_XPD,              
00194   {SH_OP_TEX,              
00195   {SH_OP_TEXI,             
00196   {SH_OP_TEXD,             
00197   {SH_OP_KIL,              
00198   {SH_OP_OPTBRA,           
00199 #endif
00200 
00201 // @todo type implement emit
00202 void CcBackendCode::emit(const ShStatement& stmt) {
00203   static CcOpCodeMap opcodeMap;
00204 
00205   // @todo type should really move this to the CcBackendCode constructor 
00206   // @todo type should handle other types properly
00207   
00208   // fill in opcodeMap from the above table
00209   if(opcodeMap.empty()) {
00210     SH_CC_DEBUG_PRINT("ShOperation -> C++ code mappings");
00211     for(int i = 0; opCodeTable[i].op != SH_OPERATION_END; ++i) {
00212 
00213       opcodeMap[opCodeTable[i].op] = CcOpCodeVecs(opCodeTable[i]); 
00214       SH_CC_DEBUG_PRINT(opInfo[opCodeTable[i].op].name << " -> " 
00215           << opcodeMap[opCodeTable[i].op].encode());
00216     }
00217   }
00218 
00219   // output SH intermediate m_code for reference
00220   m_code << "  // " << stmt << std::endl;
00221 
00222   // generate C m_code from statement
00223 
00224   // @todo get rid of warnings for assignment of different types 
00225   // (e.g. when float to int cast required)
00226 
00227   // handle ops in the table first 
00228   if(opcodeMap.find(stmt.op) != opcodeMap.end()) {
00229     CcOpCodeVecs codeVecs = opcodeMap[stmt.op]; 
00230     for(int i = 0; i < stmt.dest.size(); ++i) {
00231       m_code << "  " << resolve(stmt.dest, i) << " = (" 
00232         << ctype(stmt.dest.valueType()) << ")(";
00233       unsigned int j;
00234       for(j = 0; j < codeVecs.index.size(); ++j) { 
00235         const ShVariable& src = stmt.src[codeVecs.index[j]];
00236         m_code << codeVecs.frag[j];
00237         if(codeVecs.scalar[j]) {
00238           m_code << resolve(src, src.size() > 1 ? i : 0); 
00239         } else {
00240           m_code << resolve(src, i); 
00241         }
00242       }
00243       m_code << codeVecs.frag[j] << ");" << std::endl;
00244     }
00245     return;
00246   }
00247 
00248   // handle remaining ops with some custom code
00249   // @todo improve collecting ops
00250   switch(stmt.op) {
00251     case SH_OP_DOT:
00252       {
00253         SH_DEBUG_ASSERT(stmt.dest.size() == 1);
00254         m_code << "  " << resolve(stmt.dest, 0) << " = " 
00255           << resolve(stmt.src[0], 0) 
00256           << " * "
00257           << resolve(stmt.src[1], 0)
00258           << ";" << std::endl;
00259 
00260         int inc0 = stmt.src[0].size() == 1 ? 0 : 1;
00261         int inc1 = stmt.src[1].size() == 1 ? 0 : 1;
00262         int size = std::max(stmt.src[0].size(), stmt.src[1].size());
00263 
00264         int i, s0, s1;
00265         for(i = s0 = s1 = 1; i < size; ++i, s0 += inc0, s1 += inc1) {
00266           m_code << "  " << resolve(stmt.dest, 0) << " += " 
00267             << resolve(stmt.src[0], s0) 
00268             << " * "
00269             << resolve(stmt.src[1], s1)
00270             << ";" << std::endl;
00271         }
00272         break;
00273       }
00274 
00275     case SH_OP_CSUM:
00276       {
00277         SH_DEBUG_ASSERT(stmt.dest.size() == 1);
00278         m_code << "  " << resolve(stmt.dest, 0) << " = " 
00279           << resolve(stmt.src[0], 0) 
00280           << ";" << std::endl;
00281 
00282         int size = stmt.src[0].size();
00283         for(int i = 1; i < size; ++i) {
00284           m_code << "  " << resolve(stmt.dest, 0) << " += " 
00285             << resolve(stmt.src[0], i) 
00286             << ";" << std::endl;
00287         }
00288         break;
00289       }
00290 
00291     case SH_OP_CMUL:
00292       {
00293         SH_DEBUG_ASSERT(stmt.dest.size() == 1);
00294         m_code << "  " << resolve(stmt.dest, 0) << " = " 
00295           << resolve(stmt.src[0], 0) 
00296           << ";" << std::endl;
00297 
00298         int size = stmt.src[0].size();
00299         for(int i = 1; i < size; ++i) {
00300           m_code << "  " << resolve(stmt.dest, 0) << " *= " 
00301             << resolve(stmt.src[0], i) 
00302             << ";" << std::endl;
00303         }
00304         break;
00305       }
00306 
00307     case SH_OP_LIT:
00308       {
00309         m_code << "  {" << std::endl;
00310         
00311         // Clamp to zero the first two arguments
00312         m_code << "    " << resolve(stmt.src[0], 0) << " = (" 
00313                << resolve(stmt.src[0], 0) << " > 0) ? " 
00314                << resolve(stmt.src[0], 0) << " : 0;" << std::endl;
00315         m_code << "    " << resolve(stmt.src[0], 1) << " = (" 
00316                << resolve(stmt.src[0], 1) << " > 0) ? " 
00317                << resolve(stmt.src[0], 1) << " : 0;" << std::endl;
00318 
00319         // Clamp to -128, 128 the third argument
00320         m_code << "    " << resolve(stmt.src[0], 2) << " = (" 
00321                << resolve(stmt.src[0], 2) << " > -128.0) ? " 
00322                << resolve(stmt.src[0], 2) << " : -128.0;" << std::endl;
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 
00327         // Result according to OpenGL spec
00328         m_code << "    " << resolve(stmt.dest, 0) << " = 1;" << std::endl;
00329         m_code << "    " << resolve(stmt.dest, 1) << " = "
00330                << resolve(stmt.src[0], 0) << ";" << std::endl;
00331         m_code << "    " << resolve(stmt.dest, 2) << " = (" 
00332                << resolve(stmt.src[0], 0) << " > 0)" << " * pow(" 
00333                << resolve(stmt.src[0], 1) << ", " << resolve(stmt.src[0], 2)
00334                << ");" << std::endl;
00335         m_code << "    " << resolve(stmt.dest, 3) << " = 1;" << std::endl;
00336 
00337         m_code << "  }" << std::endl;
00338         break;
00339       }
00340 
00341     case SH_OP_NORM:
00342       {
00343         m_code << "  {" << std::endl;
00344         m_code << "    float len = 1.0/sqrt(";
00345         for(int i = 0; i < stmt.dest.size(); i++)
00346         {
00347           if (i != 0) m_code << " + ";
00348           m_code << resolve(stmt.src[0], i)
00349            << " * "
00350            << resolve(stmt.src[0], i);
00351         }
00352         m_code << ");" << std::endl;
00353         for(int i = 0; i < stmt.dest.size(); i++)
00354         {
00355           m_code << "    "
00356            << resolve(stmt.dest, i)
00357            << " = len*"
00358            << resolve(stmt.src[0], i)
00359            << ";" << std::endl;
00360         }
00361         m_code << "  }" << std::endl;
00362 
00363         break;
00364       }
00365     case SH_OP_XPD:
00366       {
00367         for(int i = 0; i < stmt.dest.size(); i++)
00368         {
00369           int i0 = (i+1)%3;
00370           int i1 = (i+2)%3;
00371           m_code << "  "
00372            << resolve(stmt.dest, i)
00373            << " = "
00374            << resolve(stmt.src[0], i0)
00375            << " * "
00376            << resolve(stmt.src[1], i1)
00377            << " - "
00378            << resolve(stmt.src[1], i0)
00379            << " * "
00380            << resolve(stmt.src[0], i1)
00381            << ";" << std::endl;
00382         }
00383 
00384         break;
00385       }
00386     case SH_OP_TEX:
00387       emitTexLookup(stmt, "sh_cc_backend_lookup");
00388       break;
00389 
00390     case SH_OP_TEXI:
00391       emitTexLookup(stmt, "sh_cc_backend_lookupi");
00392       break;
00393 
00394     case SH_OP_KIL:
00395       {
00396       // TODO: maintain prior output values 
00397       m_code << "  if (";
00398       for(int i = 0; i < stmt.src[0].size(); i++)
00399       {
00400         if (i != 0) m_code << " || ";
00401         m_code << "("
00402          << resolve(stmt.src[0], i) 
00403          << " > 0)";
00404       }
00405       m_code << ")" << std::endl;
00406       m_code << "    return;" << std::endl;
00407       break;
00408       }
00409     case SH_OP_OPTBRA:
00410       {
00411         SH_DEBUG_ASSERT(false);
00412         break;
00413       }
00414     default:
00415       {
00416       m_code << "  // *** unhandled operation "
00417              << opInfo[stmt.op].name
00418              << " ***" << std::endl;
00419       break;
00420       }
00421   }
00422 }
00423 
00424 void CcBackendCode::emitTexLookup(const ShStatement& stmt, const char* texfunc) {
00425   ShTextureNodePtr node = shref_dynamic_cast<ShTextureNode>(stmt.src[0].node());
00426   int dims = 0; 
00427   switch(node->dims()) {
00428     case SH_TEXTURE_1D: dims = 1; break;   
00429     case SH_TEXTURE_2D: dims = 2; break; 
00430     case SH_TEXTURE_RECT: dims = 2; break;
00431     case SH_TEXTURE_3D: dims = 3; break; 
00432     case SH_TEXTURE_CUBE: 
00433       SH_DEBUG_ERROR("Cube maps not handled"); 
00434     default:
00435       SH_DEBUG_ERROR("Unhandled texture dim");
00436   }
00437 
00438   // names of the functors to use for different texture lookup types 
00439   std::string srcInterp, srcFilter, srcWrap, destClamp; 
00440 
00441   if(node->traits().interpolation() != 0) {
00442       //shError(ShBackendException("cc backend supports only nearest-neighbour texture lookup."));
00443       //SH_DEBUG_WARN("cc backend supports only nearest-neighbour texture lookup.");
00444   }
00445 
00446   if (node->traits().filtering() != ShTextureTraits::SH_FILTER_NONE) {
00447       //shError(ShBackendException("cc backend does not support texture filtering."));
00448       SH_DEBUG_WARN("cc backend does not support texture filtering.");
00449   }
00450 
00451   switch(node->traits().wrapping()) {
00452     case ShTextureTraits::SH_WRAP_CLAMP:
00453     case ShTextureTraits::SH_WRAP_CLAMP_TO_EDGE:
00454       srcWrap = "sh_gcc_backend_wrap_clamp";
00455       break;
00456     case ShTextureTraits::SH_WRAP_REPEAT:
00457       srcWrap = "sh_gcc_backend_wrap_repeat";
00458       break;
00459     default:
00460       shError(ShBackendException("cc backend does not support requested texture wrapping mode."));
00461       break;
00462   }
00463 
00464   switch(node->traits().clamping()) {
00465     case ShTextureTraits::SH_CLAMPED:
00466       destClamp = "sh_gcc_backend_clamped";
00467       break;
00468     default:
00469       destClamp = "sh_gcc_backend_unclamped";
00470   }
00471 
00472   m_code << "  {" << std::endl;
00473   std::string destvar; 
00474   std::string srcvar;
00475   bool tempdest = !stmt.dest.swizzle().identity();
00476   bool tempsrc = (!stmt.src[1].swizzle().identity()) || stmt.src[1].neg();
00477 
00478   if(tempdest) {
00479     m_code << "    " << ctype(stmt.dest.valueType()) <<  
00480       " result[" << stmt.dest.size() << "];" << std::endl;
00481     destvar = "result";
00482   } else destvar = resolve(stmt.dest);
00483 
00484   if(tempsrc) {
00485     m_code << "    " << ctype(stmt.src[1].valueType()) 
00486       << " input[" << stmt.src[1].size() << "];" << std::endl;
00487 
00488     for(int i = 0; i < stmt.src[1].size(); i++) {
00489       m_code << "    input[" << i << "] = " 
00490         << resolve(stmt.src[1], i) << ";" << std::endl;
00491     }
00492 
00493     srcvar = "input";
00494   } else srcvar = resolve(stmt.src[1]);
00495 
00496   m_code << "    " << texfunc << "<"
00497      << dims << ", "
00498      << node->size() << ", "
00499      << node->width() << ", "
00500      << node->height() << ", "
00501      << node->depth() <<  ", "
00502      << ctype(node->valueType()) << "," 
00503      << srcWrap << ", "
00504      << destClamp << ">("
00505    << resolve(stmt.src[0])
00506    << ", "
00507    << srcvar
00508    << ", "
00509    << destvar
00510    << ");" << std::endl;
00511   
00512   if(tempdest) {
00513     for(int i = 0; i < stmt.dest.size(); i++) {
00514       m_code << "    "
00515        << resolve(stmt.dest, i)
00516        << " = result[" << i << "];" << std::endl;
00517     }
00518   }
00519   m_code << "  }" << std::endl;
00520 }
00521 
00522 }
00523 

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