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 
translateClampVector(const char * name,const FunctionCallInst * call,Transformer * tr,Builder * b,Module * m)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 
translateExtInst(const uint32_t extOpCode,const FunctionCallInst * call,Builder * b,Module * m)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:
BuiltinLookupTable()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 
getInstance()183   static const BuiltinLookupTable &getInstance() {
184     static BuiltinLookupTable table;
185     return table;
186   }
187 
addMapping(const char * funcName,const std::vector<std::string> & retTypes,const std::vector<std::vector<std::string>> & argTypes,const std::vector<uint8_t> & vecWidths,InstTrTy fp)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 
lookupTranslation(const char * mangled) const234   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 
runAndSerialize(Module * module,int * error)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 
transform(CapabilityInst * inst)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 
transform(ExtInstImportInst * inst)297   Instruction *transform(ExtInstImportInst *inst) override {
298     if (inst->mOperand1.compare("OpenCL.std") == 0) {
299       return nullptr;
300     }
301     return inst;
302   }
303 
transform(InBoundsPtrAccessChainInst * inst)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 
transform(SourceInst * inst)315   Instruction *transform(SourceInst *inst) override {
316     if (inst->mOperand1 == SourceLanguage::Unknown) {
317       return nullptr;
318     }
319     return inst;
320   }
321 
transform(DecorateInst * inst)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 
transform(FunctionCallInst * call)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 
CreateBuiltinPass()367 android::spirit::Pass *CreateBuiltinPass() {
368   return new android::spirit::BuiltinTransformer();
369 }
370 
371 } // namespace rs2spirv
372 
373