Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Copyright 2017, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "Builtin.h"
     18 
     19 #include "cxxabi.h"
     20 #include "spirit.h"
     21 #include "transformer.h"
     22 
     23 #include <stdint.h>
     24 
     25 #include <map>
     26 #include <string>
     27 #include <vector>
     28 
     29 namespace android {
     30 namespace spirit {
     31 
     32 namespace {
     33 
     34 Instruction *translateClampVector(const char *name,
     35                                   const FunctionCallInst *call, Transformer *tr,
     36                                   Builder *b, Module *m) {
     37   int width = name[10] - '0';
     38   if (width < 2 || width > 4) {
     39     return nullptr;
     40   }
     41 
     42   uint32_t extOpCode = 0;
     43   switch (name[strlen(name) - 1]) {
     44   case 'f':
     45     extOpCode = 43;
     46     break; // FClamp
     47   // TODO: Do we get _Z5clampDV_uuu at all? Does LLVM convert u into i?
     48   case 'u':
     49     extOpCode = 44;
     50     break; // UClamp
     51   case 'i':
     52     extOpCode = 45;
     53     break; // SClamp
     54   default:
     55     return nullptr;
     56   }
     57 
     58   std::vector<IdRef> minConstituents(width, call->mOperand2[1]);
     59   std::unique_ptr<Instruction> min(
     60       b->MakeCompositeConstruct(call->mResultType, minConstituents));
     61   tr->insert(min.get());
     62 
     63   std::vector<IdRef> maxConstituents(width, call->mOperand2[2]);
     64   std::unique_ptr<Instruction> max(
     65       b->MakeCompositeConstruct(call->mResultType, maxConstituents));
     66   tr->insert(max.get());
     67 
     68   std::vector<IdRef> extOpnds = {call->mOperand2[0], min.get(), max.get()};
     69   return b->MakeExtInst(call->mResultType, m->getGLExt(), extOpCode, extOpnds);
     70 }
     71 
     72 Instruction *translateExtInst(const uint32_t extOpCode,
     73                               const FunctionCallInst *call, Builder *b,
     74                               Module *m) {
     75   return b->MakeExtInst(call->mResultType, m->getGLExt(), extOpCode,
     76                         {call->mOperand2[0]});
     77 }
     78 
     79 } // anonymous namespace
     80 
     81 typedef std::function<Instruction *(const char *, const FunctionCallInst *,
     82                                     Transformer *, Builder *, Module *)>
     83     InstTrTy;
     84 
     85 class BuiltinLookupTable {
     86 public:
     87   BuiltinLookupTable() {
     88     for (sNameCode const *p = &mFPMathFuncOpCode[0]; p->name; p++) {
     89       const char *name = p->name;
     90       const uint32_t extOpCode = p->code;
     91       addMapping(name, {"*"}, {{"float+"}}, {1, 2, 3, 4},
     92                  [extOpCode](const char *, const FunctionCallInst *call,
     93                              Transformer *, Builder *b, Module *m) {
     94                    return translateExtInst(extOpCode, call, b, m);
     95                  });
     96     }
     97 
     98     addMapping("abs", {"*"}, {{"int+"}, {"char+"}}, {1, 2, 3, 4},
     99                [](const char *, const FunctionCallInst *call, Transformer *,
    100                   Builder *b, Module *m) {
    101                  return translateExtInst(5, call, b, m); // SAbs
    102                });
    103 
    104     addMapping("clamp", {"*"},
    105                {{"int+", "int", "int"}, {"float+", "float", "float"}},
    106                {1, 2, 3, 4}, [](const char *name, const FunctionCallInst *call,
    107                                 Transformer *tr, Builder *b, Module *m) {
    108                  return translateClampVector(name, call, tr, b, m);
    109                });
    110 
    111     addMapping("convert", {"char+", "int+", "uchar+", "uint+"},
    112                {{"char+"}, {"int+"}, {"uchar+"}, {"uint+"}}, {1, 2, 3, 4},
    113                [](const char *, const FunctionCallInst *call, Transformer *,
    114                   Builder *b, Module *) -> Instruction * {
    115                  return b->MakeUConvert(call->mResultType, call->mOperand2[0]);
    116                });
    117 
    118     addMapping(
    119         "convert", {"char+", "int+", "uchar+", "uint+"}, {{"float+"}},
    120         {1, 2, 3, 4}, [](const char *, const FunctionCallInst *call,
    121                          Transformer *, Builder *b, Module *) -> Instruction * {
    122           return b->MakeConvertFToU(call->mResultType, call->mOperand2[0]);
    123         });
    124 
    125     addMapping(
    126         "convert", {"float+"}, {{"char+"}, {"int+"}, {"uchar+"}, {"uint+"}},
    127         {1, 2, 3, 4}, [](const char *, const FunctionCallInst *call,
    128                          Transformer *, Builder *b, Module *) {
    129           return b->MakeConvertUToF(call->mResultType, call->mOperand2[0]);
    130         });
    131 
    132     addMapping("dot", {"*"}, {{"float+"}}, {1, 2, 3, 4},
    133                [](const char *, const FunctionCallInst *call, Transformer *,
    134                   Builder *b, Module *) {
    135                  return b->MakeDot(call->mResultType, call->mOperand2[0],
    136                                    call->mOperand2[1]);
    137                });
    138 
    139     addMapping("min", {"*"}, {{"uint+"}, {"uchar+"}}, {1, 2, 3, 4},
    140                [](const char *, const FunctionCallInst *call, Transformer *,
    141                   Builder *b, Module *m) {
    142                  return translateExtInst(38, call, b, m); // UMin
    143                });
    144 
    145     addMapping("min", {"*"}, {{"int+"}, {"char+"}}, {1, 2, 3, 4},
    146                [](const char *, const FunctionCallInst *call, Transformer *,
    147                   Builder *b, Module *m) {
    148                  return translateExtInst(39, call, b, m); // SMin
    149                });
    150 
    151     addMapping("max", {"*"}, {{"uint+"}, {"uchar+"}}, {1, 2, 3, 4},
    152                [](const char *, const FunctionCallInst *call, Transformer *,
    153                   Builder *b, Module *m) {
    154                  return translateExtInst(41, call, b, m); // UMax
    155                });
    156 
    157     addMapping("max", {"*"}, {{"int+"}, {"char+"}}, {1, 2, 3, 4},
    158                [](const char *, const FunctionCallInst *call, Transformer *,
    159                   Builder *b, Module *m) {
    160                  return translateExtInst(42, call, b, m); // SMax
    161                });
    162 
    163     addMapping("rsUnpackColor8888", {"*"}, {{"uchar+"}}, {4},
    164                [](const char *, const FunctionCallInst *call, Transformer *,
    165                   Builder *b, Module *m) {
    166                  auto cast = b->MakeBitcast(m->getUnsignedIntType(32),
    167                                             call->mOperand2[0]);
    168                  return b->MakeExtInst(call->mResultType, m->getGLExt(), 64,
    169                                        {cast}); // UnpackUnorm4x8
    170                });
    171 
    172     addMapping("rsPackColorTo8888", {"*"}, {{"float+"}}, {4},
    173                [](const char *, const FunctionCallInst *call, Transformer *,
    174                   Builder *b, Module *m) {
    175                  // PackUnorm4x8
    176                  auto packed = b->MakeExtInst(call->mResultType, m->getGLExt(),
    177                                               55, {call->mOperand2[0]});
    178                  return b->MakeBitcast(
    179                      m->getVectorType(m->getUnsignedIntType(8), 4), packed);
    180                });
    181   }
    182 
    183   static const BuiltinLookupTable &getInstance() {
    184     static BuiltinLookupTable table;
    185     return table;
    186   }
    187 
    188   void addMapping(const char *funcName,
    189                   const std::vector<std::string> &retTypes,
    190                   const std::vector<std::vector<std::string>> &argTypes,
    191                   const std::vector<uint8_t> &vecWidths, InstTrTy fp) {
    192     for (auto width : vecWidths) {
    193       for (auto retType : retTypes) {
    194         std::string suffixed(funcName);
    195         if (retType != "*") {
    196           if (retType.back() == '+') {
    197             retType.pop_back();
    198             if (width > 1) {
    199               retType.append(1, '0' + width);
    200             }
    201           }
    202           suffixed.append("_").append(retType);
    203         }
    204 
    205         for (auto argList : argTypes) {
    206           std::string args("(");
    207           bool first = true;
    208           for (auto argType : argList) {
    209             if (first) {
    210               first = false;
    211             } else {
    212               args.append(", ");
    213             }
    214             if (argType.front() == 'u') {
    215               argType.replace(0, 1, "unsigned ");
    216             }
    217             if (argType.back() == '+') {
    218               argType.pop_back();
    219               if (width > 1) {
    220                 argType.append(" vector[");
    221                 argType.append(1, '0' + width);
    222                 argType.append("]");
    223               }
    224             }
    225             args.append(argType);
    226           }
    227           args.append(")");
    228           mFuncNameMap[suffixed + args] = fp;
    229         }
    230       }
    231     }
    232   }
    233 
    234   InstTrTy lookupTranslation(const char *mangled) const {
    235     const char *demangled =
    236         __cxxabiv1::__cxa_demangle(mangled, nullptr, nullptr, nullptr);
    237 
    238     if (!demangled) {
    239       // All RS runtime/builtin functions are overloaded, therefore
    240       // name-mangled.
    241       return nullptr;
    242     }
    243 
    244     std::string strDemangled(demangled);
    245 
    246     auto it = mFuncNameMap.find(strDemangled);
    247     if (it == mFuncNameMap.end()) {
    248       return nullptr;
    249     }
    250     return it->second;
    251   }
    252 
    253 private:
    254   std::map<std::string, InstTrTy> mFuncNameMap;
    255 
    256   struct sNameCode {
    257     const char *name;
    258     uint32_t code;
    259   };
    260 
    261   static sNameCode constexpr mFPMathFuncOpCode[] = {
    262       {"abs", 4},        {"sin", 13},   {"cos", 14},   {"tan", 15},
    263       {"asin", 16},      {"acos", 17},  {"atan", 18},  {"sinh", 19},
    264       {"cosh", 20},      {"tanh", 21},  {"asinh", 22}, {"acosh", 23},
    265       {"atanh", 24},     {"atan2", 25}, {"pow", 26},   {"exp", 27},
    266       {"log", 28},       {"exp2", 29},  {"log2", 30},  {"sqrt", 31},
    267       {"modf", 35},      {"min", 37},   {"max", 40},   {"length", 66},
    268       {"normalize", 69}, {nullptr, 0},
    269   };
    270 
    271 }; // BuiltinLookupTable
    272 
    273 BuiltinLookupTable::sNameCode constexpr BuiltinLookupTable::mFPMathFuncOpCode[];
    274 
    275 class BuiltinTransformer : public Transformer {
    276 public:
    277   // BEGIN: cleanup unrelated to builtin functions, but necessary for LLVM-SPIRV
    278   // converter generated code.
    279 
    280   // TODO: Move these in its own pass
    281 
    282   std::vector<uint32_t> runAndSerialize(Module *module, int *error) override {
    283     module->addExtInstImport("GLSL.std.450");
    284     return Transformer::runAndSerialize(module, error);
    285   }
    286 
    287   Instruction *transform(CapabilityInst *inst) override {
    288     // Remove capabilities Address, Linkage, and Kernel.
    289     if (inst->mOperand1 == Capability::Addresses ||
    290         inst->mOperand1 == Capability::Linkage ||
    291         inst->mOperand1 == Capability::Kernel) {
    292       return nullptr;
    293     }
    294     return inst;
    295   }
    296 
    297   Instruction *transform(ExtInstImportInst *inst) override {
    298     if (inst->mOperand1.compare("OpenCL.std") == 0) {
    299       return nullptr;
    300     }
    301     return inst;
    302   }
    303 
    304   Instruction *transform(InBoundsPtrAccessChainInst *inst) override {
    305     // Transform any OpInBoundsPtrAccessChain instruction to an
    306     // OpInBoundsAccessChain instruction, since the former is not allowed by
    307     // the Vulkan validation rules.
    308     auto newInst = mBuilder.MakeInBoundsAccessChain(inst->mResultType,
    309                                                     inst->mOperand1,
    310                                                     inst->mOperand3);
    311     newInst->setId(inst->getId());
    312     return newInst;
    313   }
    314 
    315   Instruction *transform(SourceInst *inst) override {
    316     if (inst->mOperand1 == SourceLanguage::Unknown) {
    317       return nullptr;
    318     }
    319     return inst;
    320   }
    321 
    322   Instruction *transform(DecorateInst *inst) override {
    323     if (inst->mOperand2 == Decoration::LinkageAttributes ||
    324         inst->mOperand2 == Decoration::Alignment) {
    325       return nullptr;
    326     }
    327     return inst;
    328   }
    329 
    330   // END: cleanup unrelated to builtin functions
    331 
    332   Instruction *transform(FunctionCallInst *call) {
    333     FunctionInst *func =
    334         static_cast<FunctionInst *>(call->mOperand1.mInstruction);
    335     // TODO: attach name to the instruction to avoid linear search in the debug
    336     // section, i.e.,
    337     // const char *name = func->getName();
    338     const char *name = getModule()->lookupNameByInstruction(func);
    339     if (!name) {
    340       return call;
    341     }
    342 
    343     // Maps name into a SPIR-V instruction
    344     auto fpTranslate =
    345         BuiltinLookupTable::getInstance().lookupTranslation(name);
    346     if (!fpTranslate) {
    347       return call;
    348     }
    349     Instruction *inst = fpTranslate(name, call, this, &mBuilder, getModule());
    350 
    351     if (inst) {
    352       inst->setId(call->getId());
    353     }
    354 
    355     return inst;
    356   }
    357 
    358 private:
    359   Builder mBuilder;
    360 };
    361 
    362 } // namespace spirit
    363 } // namespace android
    364 
    365 namespace rs2spirv {
    366 
    367 android::spirit::Pass *CreateBuiltinPass() {
    368   return new android::spirit::BuiltinTransformer();
    369 }
    370 
    371 } // namespace rs2spirv
    372 
    373