blob: 02f7546e4aa884f1b5ef9df60ba24322a16323f6 [file] [log] [blame]
/*
* Copyright 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Builtin.h"
#include "cxxabi.h"
#include "spirit.h"
#include "transformer.h"
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
namespace android {
namespace spirit {
namespace {
Instruction *translateClampVector(const char *name,
const FunctionCallInst *call, Transformer *tr,
Builder *b, Module *m) {
int width = name[10] - '0';
if (width < 2 || width > 4) {
return nullptr;
}
uint32_t extOpCode = 0;
switch (name[strlen(name) - 1]) {
case 'f':
extOpCode = 43;
break; // FClamp
// TODO: Do we get _Z5clampDV_uuu at all? Does LLVM convert u into i?
case 'u':
extOpCode = 44;
break; // UClamp
case 'i':
extOpCode = 45;
break; // SClamp
default:
return nullptr;
}
std::vector<IdRef> minConstituents(width, call->mOperand2[1]);
std::unique_ptr<Instruction> min(
b->MakeCompositeConstruct(call->mResultType, minConstituents));
tr->insert(min.get());
std::vector<IdRef> maxConstituents(width, call->mOperand2[2]);
std::unique_ptr<Instruction> max(
b->MakeCompositeConstruct(call->mResultType, maxConstituents));
tr->insert(max.get());
std::vector<IdRef> extOpnds = {call->mOperand2[0], min.get(), max.get()};
return b->MakeExtInst(call->mResultType, m->getGLExt(), extOpCode, extOpnds);
}
Instruction *translateExtInst(const uint32_t extOpCode,
const FunctionCallInst *call, Builder *b,
Module *m) {
return b->MakeExtInst(call->mResultType, m->getGLExt(), extOpCode,
{call->mOperand2[0]});
}
} // anonymous namespace
typedef std::function<Instruction *(const char *, const FunctionCallInst *,
Transformer *, Builder *, Module *)>
InstTrTy;
class BuiltinLookupTable {
public:
BuiltinLookupTable() {
for (sNameCode const *p = &mFPMathFuncOpCode[0]; p->name; p++) {
const char *name = p->name;
const uint32_t extOpCode = p->code;
addMapping(name, {"*"}, {{"float+"}}, {1, 2, 3, 4},
[extOpCode](const char *, const FunctionCallInst *call,
Transformer *, Builder *b, Module *m) {
return translateExtInst(extOpCode, call, b, m);
});
}
addMapping("abs", {"*"}, {{"int+"}, {"char+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
return translateExtInst(5, call, b, m); // SAbs
});
addMapping("clamp", {"*"},
{{"int+", "int", "int"}, {"float+", "float", "float"}},
{1, 2, 3, 4}, [](const char *name, const FunctionCallInst *call,
Transformer *tr, Builder *b, Module *m) {
return translateClampVector(name, call, tr, b, m);
});
addMapping("convert", {"char+", "int+", "uchar+", "uint+"},
{{"char+"}, {"int+"}, {"uchar+"}, {"uint+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *) -> Instruction * {
return b->MakeUConvert(call->mResultType, call->mOperand2[0]);
});
addMapping(
"convert", {"char+", "int+", "uchar+", "uint+"}, {{"float+"}},
{1, 2, 3, 4}, [](const char *, const FunctionCallInst *call,
Transformer *, Builder *b, Module *) -> Instruction * {
return b->MakeConvertFToU(call->mResultType, call->mOperand2[0]);
});
addMapping(
"convert", {"float+"}, {{"char+"}, {"int+"}, {"uchar+"}, {"uint+"}},
{1, 2, 3, 4}, [](const char *, const FunctionCallInst *call,
Transformer *, Builder *b, Module *) {
return b->MakeConvertUToF(call->mResultType, call->mOperand2[0]);
});
addMapping("dot", {"*"}, {{"float+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *) {
return b->MakeDot(call->mResultType, call->mOperand2[0],
call->mOperand2[1]);
});
addMapping("min", {"*"}, {{"uint+"}, {"uchar+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
return translateExtInst(38, call, b, m); // UMin
});
addMapping("min", {"*"}, {{"int+"}, {"char+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
return translateExtInst(39, call, b, m); // SMin
});
addMapping("max", {"*"}, {{"uint+"}, {"uchar+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
return translateExtInst(41, call, b, m); // UMax
});
addMapping("max", {"*"}, {{"int+"}, {"char+"}}, {1, 2, 3, 4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
return translateExtInst(42, call, b, m); // SMax
});
addMapping("rsUnpackColor8888", {"*"}, {{"uchar+"}}, {4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
auto cast = b->MakeBitcast(m->getUnsignedIntType(32),
call->mOperand2[0]);
return b->MakeExtInst(call->mResultType, m->getGLExt(), 64,
{cast}); // UnpackUnorm4x8
});
addMapping("rsPackColorTo8888", {"*"}, {{"float+"}}, {4},
[](const char *, const FunctionCallInst *call, Transformer *,
Builder *b, Module *m) {
// PackUnorm4x8
auto packed = b->MakeExtInst(call->mResultType, m->getGLExt(),
55, {call->mOperand2[0]});
return b->MakeBitcast(
m->getVectorType(m->getUnsignedIntType(8), 4), packed);
});
}
static const BuiltinLookupTable &getInstance() {
static BuiltinLookupTable table;
return table;
}
void 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) {
for (auto width : vecWidths) {
for (auto retType : retTypes) {
std::string suffixed(funcName);
if (retType != "*") {
if (retType.back() == '+') {
retType.pop_back();
if (width > 1) {
retType.append(1, '0' + width);
}
}
suffixed.append("_").append(retType);
}
for (auto argList : argTypes) {
std::string args("(");
bool first = true;
for (auto argType : argList) {
if (first) {
first = false;
} else {
args.append(", ");
}
if (argType.front() == 'u') {
argType.replace(0, 1, "unsigned ");
}
if (argType.back() == '+') {
argType.pop_back();
if (width > 1) {
argType.append(" vector[");
argType.append(1, '0' + width);
argType.append("]");
}
}
args.append(argType);
}
args.append(")");
mFuncNameMap[suffixed + args] = fp;
}
}
}
}
InstTrTy lookupTranslation(const char *mangled) const {
const char *demangled =
__cxxabiv1::__cxa_demangle(mangled, nullptr, nullptr, nullptr);
if (!demangled) {
// All RS runtime/builtin functions are overloaded, therefore
// name-mangled.
return nullptr;
}
std::string strDemangled(demangled);
auto it = mFuncNameMap.find(strDemangled);
if (it == mFuncNameMap.end()) {
return nullptr;
}
return it->second;
}
private:
std::map<std::string, InstTrTy> mFuncNameMap;
struct sNameCode {
const char *name;
uint32_t code;
};
static sNameCode constexpr mFPMathFuncOpCode[] = {
{"abs", 4}, {"sin", 13}, {"cos", 14}, {"tan", 15},
{"asin", 16}, {"acos", 17}, {"atan", 18}, {"sinh", 19},
{"cosh", 20}, {"tanh", 21}, {"asinh", 22}, {"acosh", 23},
{"atanh", 24}, {"atan2", 25}, {"pow", 26}, {"exp", 27},
{"log", 28}, {"exp2", 29}, {"log2", 30}, {"sqrt", 31},
{"modf", 35}, {"min", 37}, {"max", 40}, {"length", 66},
{"normalize", 69}, {nullptr, 0},
};
}; // BuiltinLookupTable
BuiltinLookupTable::sNameCode constexpr BuiltinLookupTable::mFPMathFuncOpCode[];
class BuiltinTransformer : public Transformer {
public:
// BEGIN: cleanup unrelated to builtin functions, but necessary for LLVM-SPIRV
// converter generated code.
// TODO: Move these in its own pass
std::vector<uint32_t> runAndSerialize(Module *module, int *error) override {
module->addExtInstImport("GLSL.std.450");
return Transformer::runAndSerialize(module, error);
}
Instruction *transform(CapabilityInst *inst) override {
// Remove capabilities Address, Linkage, and Kernel.
if (inst->mOperand1 == Capability::Addresses ||
inst->mOperand1 == Capability::Linkage ||
inst->mOperand1 == Capability::Kernel) {
return nullptr;
}
return inst;
}
Instruction *transform(ExtInstImportInst *inst) override {
if (inst->mOperand1.compare("OpenCL.std") == 0) {
return nullptr;
}
return inst;
}
Instruction *transform(InBoundsPtrAccessChainInst *inst) override {
// Transform any OpInBoundsPtrAccessChain instruction to an
// OpInBoundsAccessChain instruction, since the former is not allowed by
// the Vulkan validation rules.
auto newInst = mBuilder.MakeInBoundsAccessChain(inst->mResultType,
inst->mOperand1,
inst->mOperand3);
newInst->setId(inst->getId());
return newInst;
}
Instruction *transform(SourceInst *inst) override {
if (inst->mOperand1 == SourceLanguage::Unknown) {
return nullptr;
}
return inst;
}
Instruction *transform(DecorateInst *inst) override {
if (inst->mOperand2 == Decoration::LinkageAttributes ||
inst->mOperand2 == Decoration::Alignment) {
return nullptr;
}
return inst;
}
// END: cleanup unrelated to builtin functions
Instruction *transform(FunctionCallInst *call) {
FunctionInst *func =
static_cast<FunctionInst *>(call->mOperand1.mInstruction);
// TODO: attach name to the instruction to avoid linear search in the debug
// section, i.e.,
// const char *name = func->getName();
const char *name = getModule()->lookupNameByInstruction(func);
if (!name) {
return call;
}
// Maps name into a SPIR-V instruction
auto fpTranslate =
BuiltinLookupTable::getInstance().lookupTranslation(name);
if (!fpTranslate) {
return call;
}
Instruction *inst = fpTranslate(name, call, this, &mBuilder, getModule());
if (inst) {
inst->setId(call->getId());
}
return inst;
}
private:
Builder mBuilder;
};
} // namespace spirit
} // namespace android
namespace rs2spirv {
android::spirit::Pass *CreateBuiltinPass() {
return new android::spirit::BuiltinTransformer();
}
} // namespace rs2spirv