blob: e321ee0e7a9fe5ab282969b0813446d1b3b44828 [file] [edit]
// Copyright 2019 The Clspv Authors. All rights reserved.
//
// 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 "llvm/Support/Regex.h"
#include "llvm/Support/raw_ostream.h"
#include "Builtins.h"
#include "clspv/spirv_glsl.hpp"
#include <cstdlib>
#include <unordered_map>
using namespace llvm;
using namespace clspv;
////////////////////////////////////////////////////////////////////////////////
//// Convert Builtin function name to a Type enum
////////////////////////////////////////////////////////////////////////////////
Builtins::BuiltinType
Builtins::LookupBuiltinType(const std::string &builtin_name) {
const char *clc_prefix = "__clc_";
std::string name = builtin_name;
if (name.find(clc_prefix) == 0) {
name.erase(0, strlen(clc_prefix));
}
if (name.find("spirv.op") == 0) {
return Builtins::kSpirvOp;
}
// Build static map of builtin function names
#include "BuiltinsMap.inc"
auto ii = s_func_map.find(name.c_str());
if (ii != s_func_map.end()) {
return (*ii).second;
}
return Builtins::kBuiltinNone;
}
namespace {
const std::string kPreviousParam = "__clspv_previous_param";
////////////////////////////////////////////////////////////////////////////////
// Mangled name parsing utilities
// - We only handle Itanium-style C++ mangling, plus modifications for OpenCL.
////////////////////////////////////////////////////////////////////////////////
// Given a mangled name starting at character position |pos| in |str|, extracts
// the original name (without mangling) and updates |pos| so it will index the
// character just past that original name (which might be just past the end of
// the string). If the mangling is invalid, then an empty string is returned,
// and |pos| is not updated. Example: if str = "_Z3fooi", and *pos = 2, then
// returns "foo" and adds 4 to *pos.
std::string GetUnmangledName(const std::string &str, size_t *pos) {
char *end = nullptr;
assert(*pos < str.size());
auto name_len = strtol(&str[*pos], &end, 10);
if (!name_len) {
return "";
}
ptrdiff_t name_pos = end - str.data();
if (static_cast<std::size_t>(name_pos + name_len) > str.size()) {
// Protect against maliciously large number.
return "";
}
*pos = name_pos + name_len;
auto substr = str.substr(size_t(name_pos), name_len);
if (substr != "spirv.op") {
return substr;
}
auto args_pos = str.find(".", *pos + 1);
if (args_pos == std::string::npos) {
return substr;
}
*pos = args_pos + 1;
return str.substr(size_t(name_pos), *pos - name_pos);
}
// Capture parameter type and qualifiers starting at |pos|
// - return new parsing position pos, or zero for error
size_t GetParameterType(const std::string &mangled_name,
clspv::Builtins::ParamTypeInfo *type_info, size_t pos) {
// Parse a parameter type encoding
char type_code = mangled_name[pos++];
switch (type_code) {
// qualifiers
case 'P': // Pointer
case 'R': // Reference
return GetParameterType(mangled_name, type_info, pos);
case 'k': // ??? not part of cxxabi
case 'K': // const
case 'V': // volatile
return GetParameterType(mangled_name, type_info, pos);
case 'U': { // Address space
// address_space name not captured
(void)GetUnmangledName(mangled_name, &pos);
return GetParameterType(mangled_name, type_info, pos);
}
case 'A': { // Atomic type
if (mangled_name.substr(pos, 5) == "tomic")
return GetParameterType(mangled_name, type_info, pos + 5);
return 0;
}
case 'S':
// same as previous parameter
if (mangled_name[pos] != '_') {
return 0;
}
type_info->name = kPreviousParam;
return pos + 1;
// OCL types
case 'D':
type_code = mangled_name[pos++];
if (type_code == 'v') { // OCL vector
char *end = nullptr;
int numElems = strtol(&mangled_name[pos], &end, 10);
if (!numElems) {
return 0;
}
type_info->vector_size = numElems;
pos = end - mangled_name.data();
if (pos > mangled_name.size()) {
// Protect against maliciously large number.
return 0;
}
if (mangled_name[pos++] != '_') {
return 0;
}
return GetParameterType(mangled_name, type_info, pos);
} else if (type_code == 'h') { // OCL half
type_info->type_id = Type::HalfTyID;
type_info->is_signed = true;
type_info->byte_len = 2;
return pos;
} else {
#ifdef DEBUG
llvm::outs() << "Func: " << mangled_name << "\n";
llvm_unreachable("failed to demangle name");
#endif
return 0;
}
break;
// element types
case 'l': // long
case 'i': // int
case 's': // short
case 'c': // char
case 'a': // signed char
type_info->type_id = Type::IntegerTyID;
type_info->is_signed = true;
break;
case 'm': // unsigned long
case 'j': // unsigned int
case 't': // unsigned short
case 'h': // unsigned char
type_info->type_id = Type::IntegerTyID;
type_info->is_signed = false;
break;
case 'd': // double float
case 'f': // single float
type_info->type_id = Type::FloatTyID;
type_info->is_signed = true;
break;
case 'v': // void
break;
case '1': // struct name
case '2': // - a <positive length number> for size of the following name
case '3': // - e.g. struct Foobar {} - would be encoded as '6Foobar'
case '4': // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.unqualified-name
case '5':
case '6':
case '7':
case '8':
case '9':
type_info->type_id = Type::StructTyID;
pos--;
type_info->name = GetUnmangledName(mangled_name, &pos);
break;
case '.':
return 0;
default:
#ifdef DEBUG
llvm::outs() << "Func: " << mangled_name << "\n";
llvm_unreachable("failed to demangle name");
#endif
return 0;
}
switch (type_code) {
// element types
case 'l': // long
case 'm': // unsigned long
case 'd': // double float
type_info->byte_len = 8;
break;
case 'i': // int
case 'j': // unsigned int
case 'f': // single float
type_info->byte_len = 4;
break;
case 's': // short
case 't': // unsigned short
type_info->byte_len = 2;
break;
case 'c': // char
case 'a': // signed char
case 'h': // unsigned char
type_info->byte_len = 1;
break;
default:
break;
}
return pos;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// FunctionInfo::GetFromMangledNameCheck
// - parse mangled name as far as possible. Some names are an aggregate of
// fields separated by '.'
// - extract name and parameter types, and return type for 'convert' functions
// - return true if the mangled name can be fully parsed
bool Builtins::FunctionInfo::GetFromMangledNameCheck(
const std::string &mangled_name) {
size_t pos = 0;
if (!(mangled_name[pos++] == '_' && mangled_name[pos++] == 'Z')) {
name_ = mangled_name;
return false;
}
name_ = GetUnmangledName(mangled_name, &pos);
if (name_.empty()) {
return false;
}
auto mangled_name_len = mangled_name.size();
while (pos < mangled_name_len) {
ParamTypeInfo type_info;
if (mangled_name[pos] == 'S') {
// handle duplicate param_type. Comes in two flavours:
// S_ and S#_.
char p1 = mangled_name[pos + 1];
if (p1 != '_' && (mangled_name[pos + 2] != '_')) {
return false;
}
pos += p1 == '_' ? 2 : 3;
if (params_.empty()) {
return false;
}
params_.push_back(params_.back());
} else if ((pos = GetParameterType(mangled_name, &type_info, pos))) {
if (type_info.type_id == llvm::Type::VoidTyID &&
type_info.name == kPreviousParam) {
// After additional demangling, the underlying data type is the same as
// the previous parameter.
if (!params_.empty()) {
params_.push_back(params_.back());
} else {
return false;
}
} else {
params_.push_back(type_info);
}
} else {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// FunctionInfo ctor - parses mangled name
Builtins::FunctionInfo::FunctionInfo(const std::string &mangled_name) {
is_valid_ = GetFromMangledNameCheck(mangled_name);
type_ = LookupBuiltinType(name_);
if (type_ == kConvert) {
// deduce return type from name, only for convert
char tok = name_[8];
return_type_.is_signed = tok != 'u'; // unsigned
return_type_.type_id = tok == 'f' ? Type::FloatTyID : Type::IntegerTyID;
}
}
// get const ParamTypeInfo for nth parameter
const Builtins::ParamTypeInfo &
Builtins::FunctionInfo::getParameter(size_t _arg) const {
assert(params_.size() > _arg);
return params_[_arg];
}
Builtins::ParamTypeInfo &Builtins::FunctionInfo::getParameter(size_t _arg) {
assert(params_.size() > _arg);
return params_[_arg];
}
// Test for OCL Sampler parameter type
bool Builtins::ParamTypeInfo::isSampler() const {
return type_id == Type::StructTyID &&
(name == "ocl_sampler" || name == "opencl.sampler_t");
}
llvm::Type *Builtins::ParamTypeInfo::DataType(LLVMContext &context) const {
if (isSampler()) {
llvm_unreachable("sampler is unhandled");
}
Type *ty = nullptr;
switch (type_id) {
case llvm::Type::IntegerTyID:
ty = llvm::IntegerType::get(context, byte_len * 8);
break;
case llvm::Type::HalfTyID:
ty = llvm::Type::getHalfTy(context);
break;
case llvm::Type::FloatTyID:
ty = llvm::Type::getFloatTy(context);
break;
case llvm::Type::DoubleTyID:
ty = llvm::Type::getDoubleTy(context);
break;
default:
llvm_unreachable("unsupported type");
break;
}
if (vector_size > 0) {
ty = FixedVectorType::get(ty, vector_size);
}
return ty;
}
////////////////////////////////////////////////////////////////////////////////
//// Lookup interface
//// - only demangle once for any name encountered
////////////////////////////////////////////////////////////////////////////////
const Builtins::FunctionInfo &
Builtins::Lookup(const std::string &mangled_name) {
static std::unordered_map<std::string, FunctionInfo> s_mangled_map;
auto fi = s_mangled_map.emplace(mangled_name, mangled_name);
return (*fi.first).second;
}
////////////////////////////////////////////////////////////////////////////////
// Generate a mangled name loosely based on Itanium mangling
std::string Builtins::GetMangledFunctionName(const char *name, Type *type) {
assert(name);
std::string mangled_name =
std::string("_Z") + std::to_string(strlen(name)) + name;
if (auto *func_type = dyn_cast<FunctionType>(type)) {
Type *last_arg_type = nullptr;
for (auto *arg_type : func_type->params()) {
std::string arg_name = GetMangledTypeName(arg_type);
if (arg_name.size() > 1 && arg_type == last_arg_type) {
mangled_name += "S_";
} else {
mangled_name += GetMangledTypeName(arg_type);
last_arg_type = arg_type;
}
}
} else {
mangled_name += GetMangledTypeName(type);
}
return mangled_name;
}
// The mangling follows the Itanium convention.
std::string Builtins::GetMangledFunctionName(const char *name) {
assert(name);
return std::string("_Z") + std::to_string(strlen(name)) + name;
}
std::string
Builtins::GetMangledFunctionName(const Builtins::FunctionInfo &info) {
// This is a best-effort attempt at reconstructing the mangled name for the
// given function. Because demangling is a lossy process some information may
// be lost and is therefore no longer available.
std::string name;
raw_string_ostream out(name);
StringRef function_name = info.getName();
out << "_Z" << function_name.size() << function_name;
for (size_t i = 0; i < info.getParameterCount(); ++i) {
const auto &param = info.getParameter(i);
if (param.vector_size != 0) {
out << "Dv" << param.vector_size << '_';
}
switch (param.type_id) {
case Type::FloatTyID:
case Type::HalfTyID:
switch (param.byte_len) {
case 2:
out << "Dh";
break;
case 4:
out << "f";
break;
case 8:
out << "d";
break;
default:
llvm_unreachable("Invalid byte_len for floating point type.");
break;
}
break;
case Type::IntegerTyID:
if (param.is_signed) {
switch (param.byte_len) {
case 1:
// Not enough information to distinguish between char (c) and signed
// char (a).
out << 'c';
break;
case 2:
out << "s";
break;
case 4:
out << "i";
break;
case 8:
out << "l";
break;
default:
llvm_unreachable("Invalid byte_len for signed integer type.");
break;
}
} else {
switch (param.byte_len) {
case 1:
out << 'h';
break;
case 2:
out << "t";
break;
case 4:
out << "j";
break;
case 8:
out << "m";
break;
default:
llvm_unreachable("Invalid byte_len for unsigned integer type.");
break;
}
}
break;
case Type::StructTyID:
out << param.name.size() << param.name;
break;
default:
llvm_unreachable("Unsupported type id");
break;
}
}
out.flush();
return name;
}
// The mangling loosely follows the Itanium convention.
// Its purpose is solely to ensure uniqueness of names, it is not
// meant to convey type information.
std::string Builtins::GetMangledTypeName(Type *Ty) {
std::string mangled_type_str;
switch (Ty->getTypeID()) {
case Type::VoidTyID:
return "v";
case Type::HalfTyID:
return "Dh";
case Type::FloatTyID:
return "f";
case Type::DoubleTyID:
return "d";
case Type::IntegerTyID:
switch (Ty->getIntegerBitWidth()) {
case 1:
return "b";
case 8:
return "h";
case 16:
return "t";
case 32:
return "j";
case 64:
return "m";
default:
assert(0);
break;
}
break;
case Type::StructTyID: {
auto *StrTy = cast<StructType>(Ty);
if (StrTy->isLiteral()) {
assert(StrTy->getNumElements() == 1);
return GetMangledTypeName(StrTy->getElementType(0));
}
mangled_type_str =
std::to_string(Ty->getStructName().size()) + Ty->getStructName().str();
break;
}
case Type::ArrayTyID:
mangled_type_str = "P" + GetMangledTypeName(Ty->getArrayElementType());
break;
case Type::PointerTyID: {
mangled_type_str = "P";
auto AS = Ty->getPointerAddressSpace();
if (AS != 0) {
std::string AS_name = "AS" + std::to_string(AS);
mangled_type_str += "U" + std::to_string(AS_name.size()) + AS_name;
}
break;
}
case Type::FixedVectorTyID: {
auto VecTy = cast<VectorType>(Ty);
mangled_type_str =
"Dv" + std::to_string(VecTy->getElementCount().getKnownMinValue()) +
"_" + GetMangledTypeName(VecTy->getElementType());
break;
}
case Type::FunctionTyID:
case Type::X86_FP80TyID:
case Type::FP128TyID:
case Type::PPC_FP128TyID:
case Type::LabelTyID:
case Type::MetadataTyID:
case Type::TokenTyID:
default:
assert(0);
break;
}
return mangled_type_str;
}
glsl::ExtInst
Builtins::getExtInstEnum(const Builtins::FunctionInfo &func_info) {
switch (func_info.getType()) {
case Builtins::kClamp: {
auto param_type = func_info.getParameter(0);
if (IsFloatTypeID(param_type.type_id)) {
return glsl::ExtInst::ExtInstNClamp;
}
return param_type.is_signed ? glsl::ExtInst::ExtInstSClamp
: glsl::ExtInst::ExtInstUClamp;
}
case Builtins::kMax: {
auto param_type = func_info.getParameter(0);
if (IsFloatTypeID(param_type.type_id)) {
return glsl::ExtInst::ExtInstFMax;
}
return param_type.is_signed ? glsl::ExtInst::ExtInstSMax
: glsl::ExtInst::ExtInstUMax;
}
case Builtins::kMin: {
auto param_type = func_info.getParameter(0);
if (IsFloatTypeID(param_type.type_id)) {
return glsl::ExtInst::ExtInstFMin;
}
return param_type.is_signed ? glsl::ExtInst::ExtInstSMin
: glsl::ExtInst::ExtInstUMin;
}
case Builtins::kAbs:
return glsl::ExtInst::ExtInstSAbs;
case Builtins::kFmax:
return glsl::ExtInst::ExtInstNMax;
case Builtins::kFmin:
return glsl::ExtInst::ExtInstNMin;
case Builtins::kDegrees:
return glsl::ExtInst::ExtInstDegrees;
case Builtins::kRadians:
return glsl::ExtInst::ExtInstRadians;
case Builtins::kMix:
return glsl::ExtInst::ExtInstFMix;
case Builtins::kAcos:
case Builtins::kAcospi:
return glsl::ExtInst::ExtInstAcos;
case Builtins::kAcosh:
return glsl::ExtInst::ExtInstAcosh;
case Builtins::kAsin:
case Builtins::kAsinpi:
return glsl::ExtInst::ExtInstAsin;
case Builtins::kAsinh:
return glsl::ExtInst::ExtInstAsinh;
case Builtins::kAtan:
case Builtins::kAtanpi:
return glsl::ExtInst::ExtInstAtan;
case Builtins::kAtanh:
return glsl::ExtInst::ExtInstAtanh;
case Builtins::kAtan2:
case Builtins::kAtan2pi:
return glsl::ExtInst::ExtInstAtan2;
case Builtins::kCeil:
return glsl::ExtInst::ExtInstCeil;
case Builtins::kSin:
case Builtins::kHalfSin:
case Builtins::kNativeSin:
return glsl::ExtInst::ExtInstSin;
case Builtins::kSinh:
return glsl::ExtInst::ExtInstSinh;
case Builtins::kCos:
case Builtins::kHalfCos:
case Builtins::kNativeCos:
return glsl::ExtInst::ExtInstCos;
case Builtins::kCosh:
return glsl::ExtInst::ExtInstCosh;
case Builtins::kTan:
case Builtins::kHalfTan:
case Builtins::kNativeTan:
return glsl::ExtInst::ExtInstTan;
case Builtins::kTanh:
return glsl::ExtInst::ExtInstTanh;
case Builtins::kExp:
case Builtins::kHalfExp:
case Builtins::kNativeExp:
return glsl::ExtInst::ExtInstExp;
case Builtins::kExp2:
case Builtins::kHalfExp2:
case Builtins::kNativeExp2:
return glsl::ExtInst::ExtInstExp2;
case Builtins::kLog:
case Builtins::kHalfLog:
case Builtins::kNativeLog:
return glsl::ExtInst::ExtInstLog;
case Builtins::kLog2:
case Builtins::kHalfLog2:
case Builtins::kNativeLog2:
return glsl::ExtInst::ExtInstLog2;
case Builtins::kFabs:
return glsl::ExtInst::ExtInstFAbs;
case Builtins::kFma:
return glsl::ExtInst::ExtInstFma;
case Builtins::kFloor:
return glsl::ExtInst::ExtInstFloor;
case Builtins::kLdexp:
return glsl::ExtInst::ExtInstLdexp;
case Builtins::kPow:
case Builtins::kPowr:
case Builtins::kHalfPowr:
case Builtins::kNativePowr:
return glsl::ExtInst::ExtInstPow;
case Builtins::kRint:
return glsl::ExtInst::ExtInstRoundEven;
case Builtins::kRound:
return glsl::ExtInst::ExtInstRound;
case Builtins::kSqrt:
case Builtins::kHalfSqrt:
case Builtins::kNativeSqrt:
return glsl::ExtInst::ExtInstSqrt;
case Builtins::kRsqrt:
case Builtins::kHalfRsqrt:
case Builtins::kNativeRsqrt:
return glsl::ExtInst::ExtInstInverseSqrt;
case Builtins::kTrunc:
return glsl::ExtInst::ExtInstTrunc;
case Builtins::kFrexp:
return glsl::ExtInst::ExtInstFrexp;
case Builtins::kClspvFract:
case Builtins::kFract:
return glsl::ExtInst::ExtInstFract;
case Builtins::kSign:
return glsl::ExtInst::ExtInstFSign;
case Builtins::kLength:
case Builtins::kFastLength:
return glsl::ExtInst::ExtInstLength;
case Builtins::kDistance:
case Builtins::kFastDistance:
return glsl::ExtInst::ExtInstDistance;
case Builtins::kStep:
return glsl::ExtInst::ExtInstStep;
case Builtins::kSmoothstep:
return glsl::ExtInst::ExtInstSmoothStep;
case Builtins::kCross:
return glsl::ExtInst::ExtInstCross;
case Builtins::kNormalize:
case Builtins::kFastNormalize:
return glsl::ExtInst::ExtInstNormalize;
case Builtins::kSpirvPack:
return glsl::ExtInst::ExtInstPackHalf2x16;
case Builtins::kSpirvUnpack:
return glsl::ExtInst::ExtInstUnpackHalf2x16;
case Builtins::kMad:
// Only floating-point kMad should be able to get here
return glsl::ExtInst::ExtInstFma;
default:
break;
}
// TODO: improve this by checking the intrinsic id.
if (func_info.getName().find("llvm.fmuladd.") == 0 ||
func_info.getName().find("llvm.fma.") == 0) {
return glsl::ExtInst::ExtInstFma;
}
if (func_info.getName().find("llvm.sqrt.") == 0) {
return glsl::ExtInst::ExtInstSqrt;
}
if (func_info.getName().find("llvm.trunc.") == 0) {
return glsl::ExtInst::ExtInstTrunc;
}
if (func_info.getName().find("llvm.ctlz.") == 0) {
return glsl::ExtInst::ExtInstFindUMsb;
}
if (func_info.getName().find("llvm.cttz.") == 0) {
return glsl::ExtInst::ExtInstFindILsb;
}
if (func_info.getName().find("llvm.ceil.") == 0) {
return glsl::ExtInst::ExtInstCeil;
}
if (func_info.getName().find("llvm.rint.") == 0) {
return glsl::ExtInst::ExtInstRoundEven;
}
if (func_info.getName().find("llvm.fabs.") == 0) {
return glsl::ExtInst::ExtInstFAbs;
}
if (func_info.getName().find("llvm.abs.") == 0) {
return glsl::ExtInst::ExtInstSAbs;
}
if (func_info.getName().find("llvm.floor.") == 0) {
return glsl::ExtInst::ExtInstFloor;
}
if (func_info.getName().find("llvm.asin.") == 0) {
return glsl::ExtInst::ExtInstAsin;
}
if (func_info.getName().find("llvm.sin.") == 0) {
return glsl::ExtInst::ExtInstSin;
}
if (func_info.getName().find("llvm.sinh.") == 0) {
return glsl::ExtInst::ExtInstSinh;
}
if (func_info.getName().find("llvm.acos.") == 0) {
return glsl::ExtInst::ExtInstAcos;
}
if (func_info.getName().find("llvm.cos.") == 0) {
return glsl::ExtInst::ExtInstCos;
}
if (func_info.getName().find("llvm.cosh.") == 0) {
return glsl::ExtInst::ExtInstCosh;
}
if (func_info.getName().find("llvm.atan.") == 0) {
return glsl::ExtInst::ExtInstAtan;
}
if (func_info.getName().find("llvm.atan2.") == 0) {
return glsl::ExtInst::ExtInstAtan2;
}
if (func_info.getName().find("llvm.tan.") == 0) {
return glsl::ExtInst::ExtInstTan;
}
if (func_info.getName().find("llvm.tanh.") == 0) {
return glsl::ExtInst::ExtInstTanh;
}
if (func_info.getName().find("llvm.exp.") == 0) {
return glsl::ExtInst::ExtInstExp;
}
if (func_info.getName().find("llvm.exp2.") == 0) {
return glsl::ExtInst::ExtInstExp2;
}
if (func_info.getName().find("llvm.log.") == 0) {
return glsl::ExtInst::ExtInstLog;
}
if (func_info.getName().find("llvm.log2.") == 0) {
return glsl::ExtInst::ExtInstLog2;
}
if (func_info.getName().find("llvm.pow.") == 0) {
return glsl::ExtInst::ExtInstPow;
}
if (func_info.getName().find("llvm.smax.") == 0) {
return glsl::ExtInst::ExtInstSMax;
}
if (func_info.getName().find("llvm.smin.") == 0) {
return glsl::ExtInst::ExtInstSMin;
}
if (func_info.getName().find("llvm.umax.") == 0) {
return glsl::ExtInst::ExtInstUMax;
}
if (func_info.getName().find("llvm.umin.") == 0) {
return glsl::ExtInst::ExtInstUMin;
}
if (func_info.getName().find("llvm.minnum.f") == 0) {
return glsl::ExtInst::ExtInstFMin;
}
if (func_info.getName().find("llvm.maxnum.f") == 0) {
return glsl::ExtInst::ExtInstFMax;
}
if (func_info.getName().find("llvm.minnum.v") == 0) {
return glsl::ExtInst::ExtInstFMin;
}
if (func_info.getName().find("llvm.maxnum.v") == 0) {
return glsl::ExtInst::ExtInstFMax;
}
if (func_info.getName().find("llvm.minimumnum.f") == 0) {
return glsl::ExtInst::ExtInstFMin;
}
if (func_info.getName().find("llvm.maximumnum.f") == 0) {
return glsl::ExtInst::ExtInstFMax;
}
if (func_info.getName().find("llvm.minimumnum.v") == 0) {
return glsl::ExtInst::ExtInstFMin;
}
if (func_info.getName().find("llvm.maximumnum.v") == 0) {
return glsl::ExtInst::ExtInstFMax;
}
if (func_info.getName().find("llvm.ldexp") == 0) {
return glsl::ExtInst::ExtInstLdexp;
}
return kGlslExtInstBad;
}
glsl::ExtInst
Builtins::getIndirectExtInstEnum(const Builtins::FunctionInfo &func_info) {
switch (func_info.getType()) {
case Builtins::kAcospi:
return glsl::ExtInst::ExtInstAcos;
case Builtins::kAsinpi:
return glsl::ExtInst::ExtInstAsin;
case Builtins::kAtanpi:
return glsl::ExtInst::ExtInstAtan;
case Builtins::kAtan2pi:
return glsl::ExtInst::ExtInstAtan2;
default:
break;
}
return kGlslExtInstBad;
}
glsl::ExtInst Builtins::getDirectOrIndirectExtInstEnum(
const Builtins::FunctionInfo &func_info) {
auto direct = getExtInstEnum(func_info);
if (direct != kGlslExtInstBad)
return direct;
return getIndirectExtInstEnum(func_info);
}
bool Builtins::BuiltinWithGenericPointer(StringRef name) {
StringRef prefix("_Z");
if (!name.starts_with(prefix)) {
return false;
}
size_t pos = prefix.size();
std::string demangled = GetUnmangledName(name.str(), &pos);
if (demangled.empty()) {
// It could be a local symbol (_ZLxx)
return false;
}
const char *list[] = {"fract", "frexp", "modf",
"remquo", "lgamma_r", "sincos"};
for (const auto &fn_name : list) {
if (demangled == fn_name) {
return true;
}
}
// Check for vstore_half family of functions.
if (demangled.size() >= strlen("vstore_half")) {
static Regex regex("^vstore_half(2|3|4|8|16)?(_rte|_rtz|_rtp|_rtn)?$");
if (regex.match(demangled)) {
return true;
}
}
return false;
}
bool Builtins::IsFloatTypeID(llvm::Type::TypeID type_id) {
return type_id == llvm::Type::FloatTyID || type_id == llvm::Type::HalfTyID;
}