372 lines
12 KiB
C++
372 lines
12 KiB
C++
/*
|
|
* 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
|
|
|