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

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