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